/**
 * The Interface class will get the user input, generates and send a message object to MessagePasser of the parent.
 */
import java.util.*;
import java.sql.Timestamp;
import common.*;

public class Interface extends Thread{
	private Process parent;
	private boolean running;
	
	private Scanner key_in;
	
	public Interface( Process _parent ){
		parent = _parent;
		key_in = new Scanner( System.in );
		turnoff();	// prevent the screen from getting swamped by prompts
	}
	
	/**
	 * Thread core: This does receive string input from the user, parse the input, and trigger the requested task or send a message 
	 */
	public void run(){
		while( running ){
			System.out.print( parent.myID + "$ " );
			String input = key_in.nextLine();
			
			StringTokenizer input_tokens = new StringTokenizer( input );
						
			// input handling
			if( input_tokens.hasMoreTokens() ){		// check: 1st token
				String command = null;
				
				boolean msg_created = false;
				int msg_type = Def.NOT_ASSIGNED;
				int msg_rid = Def.NOT_ASSIGNED;
				String msg_location = null;
				String msg_file = null;
				
				// decide the type of command
				command = input_tokens.nextToken();		// copy: 1st token
				command = command.trim();
				if( command.toLowerCase().equals( "help" ) ){
					System.out.println( "\n-shell: [ HELP :: Message Rules ]" );
					System.out.println( "$ ls  --  show the list of local files" );
					System.out.println( "$ ls (id)  --  show the list of remote files on (id)" );
					System.out.println( "$ say (id) (msg)  --  send a message" );
					System.out.println( "$ read (id) (location) (file)  --  read a file" );
					System.out.println( "$ write (id) (location) (file)  --  write a file" );
					System.out.println();
					//System.out.println( "$ disconnect (id)  --  disconnect the connection between me and (id)" );
					//System.out.println( "$ kill (id)  --  kill a process" );
					//System.out.println( "$ quit  --  terminate the process" );
					//System.out.println( "$ terminate  --  terminate the entire programs" );
					System.out.println();
					 
				}else if( command.toLowerCase().equals( "ls" ) ){
					if( input_tokens.hasMoreTokens() ){		// check: 2nd token
						// remote ls
						msg_type = Def.LS;
					}else{
						// local ls
						parent.print_filelist();
						continue;
					}
				}else if( command.toLowerCase().equals( "gls" ) ){
					// global ls
					parent.print_filelist();
					continue;
				}else if( command.toLowerCase().equals( "done" ) ){
					Utils.print_log( "" + Process.done );
					continue;
					
				}else if( command.toLowerCase().equals( "scheme" ) ){
					if( Conf.ME_SCHEME == Def.ME_TOKENLESS )	Utils.print_log( "Ricart-Agrawala" );
					else if( Conf.ME_SCHEME == Def.ME_TOKEN )	Utils.print_log( "Raymond" );
					continue;
					
				}else if( command.toLowerCase().equals( "say" ) ){
					msg_type = Def.MSG;
					
				}else if( command.toLowerCase().equals( "read" ) ){
					msg_type = Def.REQUEST_READ;
					
				}else if( command.toLowerCase().equals( "write" ) ){
					msg_type = Def.REQUEST_WRITE;
					
				}else if( command.toLowerCase().equals( "quit" ) ){
					System.out.println( "-proc(" + parent.myID + "): terminating" );
					//parent.shutdown();
					
				}else if( command.toLowerCase().equals( "kill" ) ){
					msg_type = Def.KILL;
					
				}else if( command.toLowerCase().equals( "disconnect" ) ){
					msg_type = Def.DISCON;
					
				}else if( command.toLowerCase().equals( "terminate" ) ){
					msg_type = Def.TERMINATE;
					
				}else if( command.toLowerCase().equals( "coor" ) ){
					Utils.print_log( "coor = " + parent.get_postman().getLowestConnected() );
					continue;
					
				}else if( command.toLowerCase().equals( "members" ) ){
					
					System.out.println( "size: " + MessagePasser.participants.size() );
					System.out.print( "member: " );
					for( int i = 0; i < MessagePasser.participants.size(); i++ ){
						System.out.print( MessagePasser.participants.get( i ) + " " );
					}
					System.out.println();
					System.out.print( "local: " );
					for( int i = 0; i < MessagePasser.local_procs.size(); i++ ){
						System.out.print( MessagePasser.local_procs.get( i ) + " " );
					}
					System.out.println();
					
					continue;
					
				}else if( command.toLowerCase().equals( "touch" ) ){
					if( input_tokens.hasMoreTokens() ){		// check: 2nd token
						String temp = input_tokens.nextToken();		// copy: 2nd token
						
						if( Utils.isNumber( temp ) ){
							msg_rid = Integer.parseInt( temp );
						}else{
							System.out.println( "-proc(" + parent.myID + "): the parameter should be a number" );
						}
						System.out.println( "-proc(" + parent.myID + "): " + parent.get_postman().isAlive( msg_rid ) );
					}else{
						System.out.println( "-proc(" + parent.myID + "): " + parent.get_postman().isAlive( parent.myID ) );
					}
					continue;
				}else if( command.toLowerCase().equals( "token" ) ){
					if( Conf.ME_SCHEME == Def.ME_TOKEN ){
						if( parent.holder == parent.myID )
							Utils.print_log( "TRUE" );
						else
							Utils.print_log( "FALSE" );
						continue;
					}
					
				}else if( command.toLowerCase().equals( "queue" ) ){
					msg_type = Def.QUEUE;
					if( !input_tokens.hasMoreTokens() ){
						Utils.print_log( "" + parent.cs_in_use );
						if( Conf.ME_SCHEME == Def.ME_TOKEN )
							Utils.print_log( "" + parent.cs_requests.size() );
						Utils.print_log( "" + parent.cs_my_requests.size() );
						continue;
					}
					
				}else if( command.toLowerCase().equals( "status" ) ){
					Measure.print_result();
				}else if( command.toLowerCase().equals( "holder" ) ){
					msg_type = Def.HOLDER;
				}else if( command.toLowerCase().equals( "garble" ) ){
					msg_type = Def.GARBLE;
				}else{
					System.out.println( "-shell: " + command + ": command not found" );
				}
				
				// decide the receiver's ID
				if( msg_type != Def.NOT_ASSIGNED ){
					if( input_tokens.hasMoreTokens() ){		// check: 2nd token
						String temp = input_tokens.nextToken();		// copy: 2nd token
						//Utils.print_log( temp );
						
						if( Utils.isNumber( temp ) ){
							msg_rid = Integer.parseInt( temp );
							//Utils.print_log( temp );
							
							// need to check the routing table, if the receiver exists
						}else if( msg_type == Def.REQUEST_READ || msg_type == Def.REQUEST_WRITE ){
							msg_rid = Def.BROADCAST;
							
							// rewind
							input_tokens = new StringTokenizer( input );
							input_tokens.nextToken();
						}
					}
					
					if( msg_rid == Def.NOT_ASSIGNED ){	// if it still doesn't have the receiver's id,
						System.out.println( "-shell: " + command + " needs (receiver_ID) as a parameter (type HELP for more instruction)" );
					}
				}//end-of-if( msg_type != Def.NOT_ASSIGNED )
				
				// more parameters - location, file
				if( msg_type == Def.REQUEST_READ || msg_type == Def.REQUEST_WRITE ){
					if( input_tokens.hasMoreTokens() ){			// check: 3rd token
						msg_location = input_tokens.nextToken();		// copy: 3rd token
					
						if( input_tokens.hasMoreTokens() ){			// check: 4th token
							msg_file = input_tokens.nextToken();		// copy: 4th token
							msg_created = true;	// REQUEST_READ and REQUEST_WRITE must have 4 paramesters
						}else
							System.out.println( "-shell: " + command + " needs (filename) as a parameter (type HELP for more instruction)" );
					}else
						System.out.println( "-shell: " + command + " needs (location) as a parameter (type HELP for more instruction)" );
					
					
				}else if( msg_type == Def.MSG ){
					if( input_tokens.hasMoreTokens() ){			// check: 3rd token
						msg_location = "";		// copy: 3rd token
						msg_file = input_tokens.nextToken();
						msg_created = true;	// MSG must have 3 paramesters
					}else
						System.out.println( "-shell: " + command + " needs (location) as a parameter (type HELP for more instruction)" );
					
				}else if( msg_type == Def.TERMINATE ){
					msg_rid = Def.BROADCAST;
					msg_created = true;
					
				}else if( msg_type != Def.NOT_ASSIGNED && msg_rid != Def.NOT_ASSIGNED ){
					msg_created = true;
				}//end-of-if( msg_type == Def.REQUEST_READ || msg_type == Def.REQUEST_WRITE )
				//
				// -- PARSING IS DONE -- //
				
				
				
				// -- create a MSG and send it -- //
				if( msg_created ){
					long msg_time_ms = System.currentTimeMillis();
					
					if( msg_type == Def.NOT_ASSIGNED )
						Utils.print_log( "ERROR: critical error (check your code @438uva) -- Interface.java" );
					
					// create a msg + send
					if( msg_type == Def.REQUEST_READ || msg_type == Def.REQUEST_WRITE ){// for broadcasting
						
						// create a msg
						Message msg = new Message( msg_type, msg_rid, parent.myID, msg_time_ms, msg_location + msg_file, null );

						// -- CREATE a LOCAL RECORD for EACH REQUEST --
						// * note: the constructor of 'CriticalSectionRecord' will generate a record
						//     for this CS request according to the information in 'msg.'
						CriticalSectionRecord cs_record = new CriticalSectionRecord( msg );		
						synchronized( parent.cs_my_requests ){
							parent.cs_my_requests.add( cs_record );
						}
						
						// **broadcast** the msg (1-to-n)
						msg.set_receiver_id( Def.BROADCAST );
						parent.get_postman().send( msg );
						
					}else{	// messages other than REQEUEST_READ || REQUEST_WRITE
						// create a msg
						Message msg = new Message( msg_type, msg_rid, parent.myID, msg_time_ms, msg_location + msg_file, null );
						// send the msg (1-to-1)
						parent.get_postman().send( msg );
						
					}
					
					System.out.println( "-shell: your message is sent" );
					
				}//end-of-if( msg_created )
			}//end-of-if( input_tokens.hasMoreTokens() )
		}//end-of-while
	}//end-of-run()
	
	// displays the text message from the other nodes
	public void show_message( Message msg ){
		System.out.println( "-shell: You've got a message from (" + msg.get_sender_id() + "): \""
							+ msg.get_content() + "\" [" + new Timestamp( msg.get_timestamp() ) + "]" );
	}
	
	// These turn on/off the user interface of a node (required for simulation with multiple nodes, to make all nodes shut up but one talk). 
	public void turnoff(){ running = false; }
	public void turnon(){ running = true; start(); }
	public boolean status(){ return running; }
}//end-of-class
